#ifndef __ECONOMY_LAND_H__
#define __ECONOMY_LAND_H__

#include <map>
#include "GameSingleton.h"
#include "Engine/GameGuid.h"
#include "Expand/IPlugin.h"
#include "Engine/Point2.hpp"
#include "GameType.h"


namespace PocketSurvival
{
    class GameManager;
    class PluginsManager;
    class AuthPlugin;
    class SubsystemPlayers;
}

using namespace PocketSurvival;
using namespace PocketSurvival::Engine;

namespace Economy
{
    enum class LandType
    {
        NeedBuyLand,            // 由管理员定义某块土皮的售价，玩家租用后才能破坏
        NeedBuyLandLimit,       // 由管理员定义某块土皮的售价，玩家租用后才能破坏，玩家只能购买或租用一块地
        LimitLand,              // 非权限玩家或管理员不可破坏
    };

    // 玩家租地
    struct UserHireLand
    {
        // 租用时所需要要的钱
        uint32_t price;
        // 租金
        uint32_t hireMoney;
        // 剩余使用的时间(分钟)
        uint32_t canUseTime;
        // 玩家对应的Guid
        GameGuid userGuid;
        // 领地起始位置，chunk位置使用的是区域内部位置
        Point2 chunkPointStart;
        // 领地终点位置，chunk位置使用的是区域内部位置，终点位置必须比起点位置的坐标大
        Point2 chunkPointEnd;
        // 有操作权限的玩家
        std::vector<GameGuid> inviteUserVec;

        UserHireLand() = default;
        inline explicit UserHireLand(uint32_t thePrice, uint32_t theHireMoney, uint32_t theCanUseTime, const GameGuid &theUserGuid, const Point2 &startPoint, const Point2 &endPoint) : 
            price(thePrice), hireMoney(theHireMoney), canUseTime(theCanUseTime), userGuid(theUserGuid), chunkPointStart(startPoint), chunkPointEnd(endPoint) {}

        friend inline void to_json(nlohmann::json &json, const UserHireLand &userHireLand)
        {
            json = {
                {"price", userHireLand.price},
                {"hireMoney", userHireLand.hireMoney},
                {"canUseTime", userHireLand.canUseTime},
                {"userGuid", userHireLand.userGuid},
                {"chunkPointStart", userHireLand.chunkPointStart},
                {"chunkPointEnd", userHireLand.chunkPointEnd},
                {"inviteUser", userHireLand.inviteUserVec}};
        }
        friend inline void from_json(const nlohmann::json &json, UserHireLand &userHireLand)
        {
            userHireLand.price = json["price"].get<uint32_t>();
            userHireLand.hireMoney = json["hireMoney"].get<uint32_t>();
            userHireLand.canUseTime = json["canUseTime"].get<uint32_t>();
            userHireLand.userGuid = json["userGuid"].get<GameGuid>();
            userHireLand.chunkPointStart = json["chunkPointStart"].get<Point2>();
            userHireLand.chunkPointEnd = json["chunkPointEnd"].get<Point2>();
            userHireLand.inviteUserVec = json["inviteUser"].get<std::vector<GameGuid>>();
        }
    };

    // 出租领地
    struct ShellLand
    {
        // 租用时所需要要的钱
        uint32_t price;
        // 后续租金，若后续的租金为0，那么这块领地可以算是永久的
        uint32_t hireMoney;
        // 领地起始位置，chunk位置使用的是区域内部位置
        Point2 chunkPointStart;
        // 领地终点位置，chunk位置使用的是区域内部位置，终点位置必须比起点位置的坐标大
        Point2 chunkPointEnd;

        friend inline void to_json(nlohmann::json &json, const ShellLand &shellHireLand)
        {
            json = {
                {"price", shellHireLand.price},
                {"hireMoney", shellHireLand.hireMoney},
                {"chunkPointStart", shellHireLand.chunkPointStart},
                {"chunkPointEnd", shellHireLand.chunkPointEnd}};
        }
        friend inline void from_json(const nlohmann::json &json, ShellLand &shellHireLand)
        {
            shellHireLand.price = json["price"].get<uint32_t>();
            shellHireLand.hireMoney = json["hireMoney"].get<uint32_t>();
            shellHireLand.chunkPointStart = json["chunkPointStart"].get<Point2>();
            shellHireLand.chunkPointEnd = json["chunkPointEnd"].get<Point2>();
        }
    };

    // 区域领地数据
    struct LandData
    {
        // 区域内部锁
        std::mutex landDataMutex;

        // 区域的领地模式
        LandType mode;
        // 拥有权限的玩家
        std::vector<GameGuid> authUserVec;
        // 玩家租地数据
        std::vector<UserHireLand> userLandVec;
        // 出租领地数据
        std::vector<ShellLand> shellLandVec;

        LandData() = default;
        explicit inline LandData(LandType type) : mode(type) {}

        inline LandData(const LandData& right)
        {
            mode = right.mode;
            authUserVec = right.authUserVec;
            userLandVec = right.userLandVec;
            shellLandVec = right.shellLandVec;
        }

        friend inline void to_json(nlohmann::json &json, const LandData &landData)
        {
            json = {
                {"mode", (uint8_t)landData.mode},
                {"authUser", landData.authUserVec},
                {"userHireLand", landData.userLandVec},
                {"shellHireLand", landData.shellLandVec}};
        }
        friend inline void from_json(const nlohmann::json &json, LandData &landData)
        {
            landData.mode = (LandType)json["mode"].get<uint8_t>();
            landData.authUserVec = json["authUser"].get<std::vector<GameGuid>>();
            landData.userLandVec = json["userHireLand"].get<std::vector<UserHireLand>>();
            landData.shellLandVec = json["shellHireLand"].get<std::vector<ShellLand>>();
        }
    };

    class EconomyLand : public SimpleSingletonItem
    {
        friend class PocketSurvival::SimpleSingleton;
    private:
        SubsystemPlayers *m_subsystemPlayers;

        AuthPlugin *m_authPlugin;
        
        std::map<std::string, LandData> m_landMap;
        std::mutex m_landMapMutex;

        // 出租领地一次出租可使用的时间
        uint32_t m_reletUseTime;

    private:
        EconomyLand() = default;
        ~EconomyLand();
        EconomyLand(const EconomyLand &) = delete;
        EconomyLand(EconomyLand &&) = delete;
        EconomyLand &operator=(const EconomyLand &) = delete;
        EconomyLand &operator=(EconomyLand &&) = delete;

        void sendHelpMessage(IPlugin::ChatObj &chatObj);
        void sendGuidMessage(const GameGuid& gameGuid, const std::string &message);
        void sendPlayerMessage(PlayerDataPtr &playerDataPtr, const std::string &message);

    public:
        void load();
        void save();


        /**
         * @brief 获取区域的领地数据
         * 
         * @note 注意：操作outLandData时，该数据可能会被其它线程删除而导致服务器奔溃的情况
         * 
         * @param regionX 对应的区域X轴
         * @param regionY 对应的区域Y轴
         * @param outLandData 区域的领地数据
         * 
         * @return 是否存在领地数据
        */
        bool getLandData(int32_t regionX, int32_t regionY, LandData *outLandData);

        /**
         * @brief 获取领地的售卖价格
         * 
         * @param chunkX 对应的区块X轴，使用全局大小
         * @param chunkY 对应的区块Y轴，使用全局大小
         * @param outMoney 领地的价格
         * 
         * @return true:成功 | false:失败
        */
        bool getShellLandPrice(int32_t chunkX, int32_t chunkY, uint32_t &outMoney);

        /**
         * @brief 获取领地现在的使用者
         * 
         * @param chunkX 对应的区块X轴，使用全局大小
         * @param chunkY 对应的区块Y轴，使用全局大小
         * @param outGameGuid 使用者的Guid
         * 
         * @return true:成功 | false:失败
        */
        bool getLandUserGuid(int32_t chunkX, int32_t chunkY, GameGuid &outGameGuid);

        /**
         * @brief 判断该玩家在该区域上是否又权限进行修改
         * 
         * @param regionX 对应的区块X轴，使用全局大小
         * @param regionY 对应的区块Y轴，使用全局大小
         * @param userGuid 玩家的Guid
         * 
         * @return true:可以操作 | false:不可操作
        */
        bool haveAuth(int32_t regionX, int32_t regionY, const GameGuid &userGuid);

        /**
         * @brief 给玩家在区域领地上的操作权限
         * 
         * @param regionX 对应的区块X轴，使用全局大小
         * @param regionY 对应的区块Y轴，使用全局大小
         * @param userGuid 玩家的Guid
         * 
         * @return true:添加成功 | false:添加失败，没有区域领地数据
        */
        bool addAuth(int32_t regionX, int32_t regionY, const GameGuid &userGuid);

        /**
         * @brief 移除玩家在区域领地上的操作权限
         * 
         * @param regionX 对应的区块X轴，使用全局大小
         * @param regionY 对应的区块Y轴，使用全局大小
         * @param userGuid 玩家的Guid
         * 
         * @return true:移除成功 | false:移除失败
        */
        bool removeAuth(int32_t regionX, int32_t regionY, const GameGuid &userGuid);
        
        /**
         * @brief 获取区域的领地模式
         * 
         * @param regionX 对应的区域X轴
         * @param regionY 对应的区域Y轴
         * @param outType 使用者的Guid
         * 
         * @return true:成功 | false:该区域没有领地数据
        */
        bool getLandDataMode(int32_t regionX, int32_t regionY, LandType &outType);

        /**
         * @brief 设置区域的领地模式
         * 
         * @note 若该区域没有领地数据，则会新加领地数据，领地数据也是由该函数进行添加的
         * 
         * @param regionX 对应的区域X轴
         * @param regionY 对应的区域Y轴
         * @param type 区域的领地模式
         * 
        */
        void setLandDataMode(int32_t regionX, int32_t regionY, LandType type);

        /**
         * @brief 移除对应区域的领地数据
         * 
         * @note 如果有玩家在该区域上租用了领地，这些数据也会被清空
         * 
         * @param regionX 对应的区域X轴
         * @param regionY 对应的区域Y轴
         * 
        */
        bool removeLandData(int32_t regionX, int32_t regionY);

        /**
         * @brief 将售卖领地转成玩家的领地
         * 
         * @param chunkX 对应的区块X轴，使用全局大小
         * @param chunkY 对应的区块Y轴，使用全局大小
         * @param userGuid 玩家的GameGuid
         * 
         * @return true:成功 | false:失败
        */
        bool shellLandToUserLand(int32_t chunkX, int32_t chunkY, const GameGuid &userGuid);

        /**
         * @brief 重新现在玩家领地的所有者
         * 
         * @param chunkX 对应的区块X轴，使用全局大小
         * @param chunkY 对应的区块Y轴，使用全局大小
         * @param userGuid 玩家的GameGuid
         * 
         * @return true:成功 | false:失败
        */
        bool userLandSetNewUser(int32_t chunkX, int32_t chunkY, const GameGuid &userGuid);

        void processCmd(IPlugin::ChatObj &chatObj, std::vector<std::string> &cmdDataVec);

    };
    
} // namespace Economy

#endif
